/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package br.com.pangalacteos.dao;

import br.com.pangalacteos.modelo.DBField;
import br.com.pangalacteos.modelo.DBFieldType;
import br.com.pangalacteos.util.HibernateUtil;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Restrictions;

/**
 * Classe genérica para acesso aos dados (DAO).
 * Algumas operações desta classe usam Transaction apenas para não ser necessário
 * a configuração do autocommit para true.
 * @author Leandro Del Sole
 */
public class HibernateDAO<T> {
    
    /**
     * Salva um objeto no banco de dados
     * @param Objeto para ser salvo no banco
     * @throws java.lang.Exception
     */
    public void save(T objeto) throws Exception {
        Session sess = HibernateUtil.getSession();
        Transaction tx = null;
        try {
            tx = sess.beginTransaction();
            
            sess.save(objeto);
            
            tx.commit();
        }
        finally {
            if (sess.isOpen()) sess.close();
        }
    }
    
    public void saveOrUpdate(T objeto) throws Exception {
        Session sess = HibernateUtil.getSession();
        Transaction tx = null;
        try {
            tx = sess.beginTransaction();
            
            sess.saveOrUpdate(objeto);
            
            tx.commit();
        }
        finally {
            if (sess.isOpen()) sess.close();
        }
    }
    
    public List<T> findByField(Class clazz, DBField dbField, Object procurarPor) throws Exception {
        Session sess = HibernateUtil.getSession();
        try {
            Criteria criteria = sess.createCriteria(clazz);
            //if (procurarPor instanceof Integer || procurarPor instanceof Date){
            if (dbField.getDbFieldType() == DBFieldType.INTEGER || 
                    dbField.getDbFieldType() == DBFieldType.DATE ||
                    dbField.getDbFieldType() == DBFieldType.FLOAT){
                System.out.println("Restrição integer/date/float");
                //criteria.add(Restrictions.eq(dbField, procurarPor));
                criteria.add(Restrictions.eq(dbField.getName(), procurarPor));
            }
            if (dbField.getDbFieldType() == DBFieldType.STRING){
                System.out.println("Restrição String");
                criteria = criteria.add(Restrictions.ilike(dbField.getName(), "%" + procurarPor + "%"));
            }
            if (dbField.getDbFieldType() == DBFieldType.FORNECEDOR){
                System.out.println("Restrição Fornecedor");
                criteria = criteria.add(Restrictions.eq(dbField.getName(), procurarPor));
            }
            
            List<T> encontrados = criteria.list();
            System.out.println("[HibernateDAO][findByField] encontrado: " + encontrados);
            
            if (encontrados == null) return Collections.emptyList();
            
            return encontrados;
        }
        finally {
            if (sess.isOpen()) sess.close();
        }        
    }
    /*
    public List<T> findByField(Class clazz, String campo, String procurarPor, Map<String, String> dbAsValor) throws Exception {
        Session sess = HibernateUtil.getSession();
        try {
            Criteria criteria = sess.createCriteria(clazz);
            ProjectionList projectionList = Projections.projectionList();
            //criteria.setProjection(Projections.projectionList().add(Projections.property("nmfornecedor"),"nmfornecedor"));
            System.out.println("[HibernateDao][findByField] Definindo projectionList");
            for (Entry<String, String> entry : dbAsValor.entrySet()){
                projectionList.add(Projections.property(entry.getKey()), entry.getValue());
            }
            criteria.setProjection(projectionList);
            System.out.println("[HibernateDao][findByField] Adicionando restrição like");
            criteria = criteria.add(Restrictions.ilike(campo, "%" + procurarPor + "%"));
            
            //não esquecer de por isso, ele que converte para a classe passada como param
            criteria.setResultTransformer(new AliasToBeanResultTransformer(clazz));
            
            List<T> encontrados = criteria.list();
            
            
            if (encontrados == null) return Collections.emptyList();
            
            return encontrados;
        }
        finally {
            if (sess.isOpen()) sess.close();
        }        
    }
    public List<T> findByExample(T objeto){
        Session sess = HibernateUtil.getSession();
        try{
            Criteria criteria = sess.createCriteria(objeto.getClass());
            criteria.add(Example.create(objeto).enableLike().ignoreCase());
            
            List<T> encontrados = criteria.list();
            
            if (encontrados == null) return Collections.emptyList();
            
            return encontrados;
        } finally {
            if (sess.isOpen()) sess.close();
        }
    }
    */
    public boolean contains(Class clazz, Object id){
        Session sess = HibernateUtil.getSession();
        try{
            Criteria criteria = sess.createCriteria(clazz);
            criteria.add(Restrictions.idEq(id));
            List resultado = criteria.list();
            System.out.println("[HibernateDAO][contains] resultado: " + resultado);
            return !resultado.isEmpty();
        } finally {
            if (sess.isOpen()) sess.close();
        }
    }
    
    public T findById(Class clazz, Object id) throws Exception {
        Session sess = HibernateUtil.getSession();
        try{
            Criteria criteria = sess.createCriteria(clazz);
            criteria.add(Restrictions.idEq(id));
            List<T> resultado = criteria.list();
            System.out.println("[HibernateDAO][findById] resultado: " + resultado);
            if (resultado.size() == 1){
                return resultado.get(0);
            } else if (resultado.size() == 0){
                return null;
            } else {
                throw new Exception("FindById retornou diferente de 0 ou 1");
            }
        } finally {
            if (sess.isOpen()) sess.close();
        }
    }
    /*
    public boolean delete(T objeto){
        Session sess = HibernateUtil.getSession();
        Transaction tx = null;
        try {
            tx = sess.beginTransaction();
            
            sess.delete(objeto);
            
            tx.commit();
            
            return true;
        } catch (Exception e){
            System.out.println("[HibernateDAO][delete] Falha na deleção do objeto: " + e);
            return false;
        }
        finally {
            if (sess.isOpen()) sess.close();
        }
    }
    */
    public void delete(T objeto) throws Exception {
        Session sess = HibernateUtil.getSession();
        Transaction tx = null;
        try {
            tx = sess.beginTransaction();
            
            sess.delete(objeto);
            
            tx.commit();
            
        } catch (Exception e){
            System.out.println("[HibernateDAO][delete] Falha na deleção do objeto: " + e);
            throw new Exception("Falha na remoção do fornecedor");
        }
        finally {
            if (sess.isOpen()) sess.close();
        }
    }
    
    public void delete(List<T> listaObjetos) throws Exception {
        Session sess = HibernateUtil.getSession();
        Transaction tx = null;
        try {
            tx = sess.beginTransaction();
            
            Iterator<T> it = listaObjetos.iterator();
            while(it.hasNext()){
                sess.delete(it.next());
            }
            tx.commit();
            
        } catch (Exception e){
            System.out.println("[HibernateDAO][delete] Falha na deleção do objeto: " + e);
            throw new Exception("Deleção falhou!");
        }
        finally {
            if (sess.isOpen()) sess.close();
        }
    }
    
    public boolean update(T objeto){
        Session sess = HibernateUtil.getSession();
        Transaction tx = null;
        try {
            tx = sess.beginTransaction();
            
            sess.update(objeto);
            
            tx.commit();
            
            return true;
        } catch (Exception e){
            System.out.println("[HibernateDAO][update] Falha na atualização do objeto: " + e);
            return false;
        }
        finally {
            if (sess.isOpen()) sess.close();
        }
    }
}
